home *** CD-ROM | disk | FTP | other *** search
- /* bashline.c -- Bash's interface to the readline library. */
-
- /* Copyright (C) 1987,1991 Free Software Foundation, Inc.
-
- This file is part of GNU Bash, the Bourne Again SHell.
-
- Bash is free software; you can redistribute it and/or modify it
- under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 1, or (at your option)
- any later version.
-
- Bash is distributed in the hope that it will be useful, but WITHOUT
- ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
- or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
- License for more details.
-
- You should have received a copy of the GNU General Public License
- along with Bash; see the file COPYING. If not, write to the Free
- Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
-
- #include <stdio.h>
- #include <readline/readline.h>
- #include <readline/history.h>
- #include "shell.h"
- #include "builtins.h"
-
- /* Functions bound to keys in Readline for Bash users. */
- static void
- shell_expand_line (), insert_last_arg (), display_shell_version (),
- operate_and_get_next ();
-
- /* Helper functions for Readline. */
- static void
- bash_symbolic_link_hook (), filename_completion_ignore (), bash_push_line ();
-
- static char
- **attempt_shell_completion (), *variable_completion_function (),
- *hostname_completion_function (), *command_word_completion_function ();
-
- static void
- snarf_hosts_from_file (), add_host_name (), sort_hostname_list ();
-
- /* Externally defined functions used by this file. */
- extern char
- *get_string_value (), *filename_completion_function (),
- *username_completion_function ();
-
- extern int show_shell_version ();
-
- #define DYNAMIC_HISTORY_COMPLETION
- #if defined (DYNAMIC_HISTORY_COMPLETION)
- static void dynamic_complete_history ();
- #endif /* DYNAMIC_HISTORY_COMPLETION */
-
- /* SPECIFIC_COMPLETION_FUNCTIONS specifies that we have individual
- completion functions which indicate what type of completion should be
- done (at or before point) that can be bound to key sequences with
- the readline library. */
- #define SPECIFIC_COMPLETION_FUNCTIONS
-
- #if defined (SPECIFIC_COMPLETION_FUNCTIONS)
- static void
- bash_specific_completion (),
- bash_complete_filename (), bash_possible_filename_completions (),
- bash_complete_filename_internal (),
- bash_complete_username (), bash_possible_username_completions (),
- bash_complete_username_internal (),
- bash_complete_hostname (), bash_possible_hostname_completions (),
- bash_complete_hostname_internal (),
- bash_complete_variable (), bash_possible_variable_completions (),
- bash_complete_variable_internal (),
- bash_complete_command (), bash_possible_command_completions (),
- bash_complete_command_internal ();
- #endif /* SPECIFIC_COMPLETION_FUNCTIONS */
-
- /* Non-zero once initalize_readline () has been called. */
- int bash_readline_initialized = 0;
-
- /* Called once from parse.y if we are going to use readline. */
- initialize_readline ()
- {
- if (bash_readline_initialized)
- return;
-
- rl_terminal_name = get_string_value ("TERM");
- rl_instream = stdin, rl_outstream = stderr;
- rl_special_prefixes = "$@%";
-
- /* Allow conditional parsing of the ~/.inputrc file. */
- rl_readline_name = "Bash";
-
- /* Bind up our special shell functions. */
- rl_add_defun
- ("shell-expand-line", (Function *)shell_expand_line, META(CTRL('E')));
-
- rl_add_defun
- ("insert-last-argument", (Function *)insert_last_arg, META('.'));
-
- rl_bind_key (META('_'), (Function *)insert_last_arg);
-
- rl_add_defun
- ("operate-and-get-next", (Function *)operate_and_get_next, CTRL('O'));
-
- rl_add_defun
- ("display-shell-version", (Function *)display_shell_version, -1);
-
- rl_bind_key_in_map
- (CTRL ('V'), (Function *)display_shell_version, emacs_ctlx_keymap);
-
- /* In Bash, the user can switch editing modes with "set -o [vi emacs]",
- so it is not necessary to allow C-M-j for context switching. Turn
- off this occasionally confusing behaviour. */
- rl_unbind_key_in_map (CTRL('J'), emacs_meta_keymap);
-
- #ifdef SPECIFIC_COMPLETION_FUNCTIONS
- rl_add_defun ("complete-filename", bash_complete_filename, META('/'));
- rl_add_defun ("possible-filename-completions",
- bash_possible_filename_completions, -1);
- rl_bind_key_in_map ('/', bash_possible_filename_completions,
- emacs_ctlx_keymap);
-
- rl_add_defun ("complete-username", bash_complete_username, META('~'));
- rl_add_defun ("possible-username-completions",
- bash_possible_username_completions, -1);
- rl_bind_key_in_map ('~', bash_possible_username_completions,
- emacs_ctlx_keymap);
-
- rl_add_defun ("complete-hostname", bash_complete_hostname, META('@'));
- rl_add_defun ("possible-hostname-completions",
- bash_possible_hostname_completions, -1);
- rl_bind_key_in_map ('@', bash_possible_hostname_completions,
- emacs_ctlx_keymap);
-
- rl_add_defun ("complete-variable", bash_complete_variable, META('$'));
- rl_add_defun ("possible-variable-completions",
- bash_possible_variable_completions, -1);
- rl_bind_key_in_map ('$', bash_possible_variable_completions,
- emacs_ctlx_keymap);
-
- rl_add_defun ("complete-command", bash_complete_command, META('!'));
- rl_add_defun ("possible-command-completions",
- bash_possible_command_completions, -1);
- rl_bind_key_in_map ('!', bash_possible_command_completions,
- emacs_ctlx_keymap);
-
- #endif /* SPECIFIC_COMPLETION_FUNCTIONS */
-
- #if defined (DYNAMIC_HISTORY_COMPLETION)
- rl_add_defun
- ("dynamic-complete-history", dynamic_complete_history, META(TAB));
- #endif /* DYNAMIC_HISTORY_COMPLETION */
-
- /* Tell the completer that we want a crack first. */
- rl_attempted_completion_function = (Function *)attempt_shell_completion;
-
- /* Tell the completer that we might want to follow symbolic links. */
- rl_symbolic_link_hook = (Function *)bash_symbolic_link_hook;
-
- /* Tell the filename completer we want a chance to ignore some names. */
- rl_ignore_some_completions_function =
- (Function *)filename_completion_ignore;
-
- bash_readline_initialized = 1;
- }
-
- /* On Sun systems at least, rl_attempted_completion_function can end up
- getting set to NULL, and rl_completion_entry_function set to do command
- word completion if Bash is interrupted while trying to complete a command
- word. This just resets all the completion functions to the right thing.
- It's called from throw_to_top_level(). */
- bashline_reinitialize ()
- {
- extern void tilde_initialize ();
-
- tilde_initialize ();
- rl_attempted_completion_function = (Function *)attempt_shell_completion;
- rl_symbolic_link_hook = (Function *)bash_symbolic_link_hook;
- }
-
- /* Contains the line to push into readline. */
- static char *push_to_readline = (char *)NULL;
-
- /* Push the contents of push_to_readline into the
- readline buffer. */
- static void
- bash_push_line ()
- {
- if (push_to_readline)
- {
- rl_insert_text (push_to_readline);
- free (push_to_readline);
- push_to_readline = (char *)NULL;
- }
- }
-
- /* Call this to set the initial text for the next line to read
- from readline. */
- int
- bash_re_edit (line)
- char *line;
- {
- if (push_to_readline)
- free (push_to_readline);
-
- push_to_readline = savestring (line);
- rl_startup_hook = (Function *)bash_push_line;
-
- return (0);
- }
-
- static void
- display_shell_version (count, c)
- int count, c;
- {
- crlf ();
- show_shell_version ();
- putc ('\r', rl_outstream);
- fflush (rl_outstream);
- rl_on_new_line ();
- rl_redisplay ();
- }
-
- /* **************************************************************** */
- /* */
- /* Readline Stuff */
- /* */
- /* **************************************************************** */
-
- /* If the user requests hostname completion, then simply build a list
- of hosts, and complete from that forever more. */
- #if !defined (ETCHOSTS)
- #define ETCHOSTS "/etc/hosts"
- #endif
-
- /* The kept list of hostnames. */
- static char **hostname_list = (char **)NULL;
-
- /* The physical size of the above list. */
- static int hostname_list_size = 0;
-
- /* The length of the above list. */
- static int hostname_list_length = 0;
-
- /* Whether or not HOSTNAME_LIST has been initialized. */
- int hostname_list_initialized = 0;
-
- /* Non-zero means that HOSTNAME_LIST needs to be sorted. */
- static int hostname_list_needs_sorting = 0;
-
- /* Initialize the hostname completion table. */
- static void
- initialize_hostname_list ()
- {
- char *temp = get_string_value ("hostname_completion_file");
-
- if (!temp)
- temp = ETCHOSTS;
-
- snarf_hosts_from_file (temp);
- sort_hostname_list ();
- if (hostname_list)
- hostname_list_initialized++;
- }
-
- /* Add NAME to the list of hosts. */
- static void
- add_host_name (name)
- char *name;
- {
- if (hostname_list_length + 2 > hostname_list_size)
- {
- hostname_list = (char **)
- xrealloc (hostname_list,
- (1 + (hostname_list_size += 100)) * sizeof (char *));
- }
-
- hostname_list[hostname_list_length] = savestring (name);
- hostname_list[++hostname_list_length] = (char *)NULL;
- hostname_list_needs_sorting++;
- }
-
- /* After you have added some names, you should sort the list of names. */
- static void
- sort_hostname_list ()
- {
- if (hostname_list_needs_sorting && hostname_list)
- sort_char_array (hostname_list);
- hostname_list_needs_sorting = 0;
- }
-
- #define cr_whitespace(c) ((c) == '\r' || (c) == '\n' || whitespace(c))
-
- static void
- snarf_hosts_from_file (filename)
- char *filename;
- {
- FILE *file = fopen (filename, "r");
- char *temp, buffer[256], name[256];
- register int i, start;
-
- if (!file)
- return;
-
- while (temp = fgets (buffer, 255, file))
- {
- /* Skip to first character. */
- for (i = 0; buffer[i] && cr_whitespace (buffer[i]); i++);
-
- /* If comment, ignore. */
- if (buffer[i] == '#')
- continue;
-
- /* If `preprocessor' directive, do the include. */
- if (strncmp (&buffer[i], "$include ", 9) == 0)
- {
- char *includefile = &buffer[i + 9];
- char *t;
-
- /* Find start of filename. */
- while (*includefile && whitespace (*includefile))
- includefile++;
-
- t = includefile;
-
- /* Find end of filename. */
- while (*t && !cr_whitespace (*t))
- t++;
-
- *t = '\0';
-
- snarf_hosts_from_file (includefile);
- continue;
- }
-
- /* Skip internet address. */
- for (; buffer[i] && !cr_whitespace (buffer[i]); i++);
-
- /* Gobble up names. Each name is separated with whitespace. */
- while (buffer[i] && buffer[i] != '#')
- {
- for (; i && cr_whitespace (buffer[i]); i++);
- if (buffer[i] == '#')
- continue;
- for (start = i; buffer[i] && !cr_whitespace (buffer[i]); i++);
- if ((i - start) == 0)
- continue;
- strncpy (name, buffer + start, i - start);
- name[i - start] = '\0';
- add_host_name (name);
- }
- }
- fclose (file);
- }
-
- /* Return a NULL terminated list of hostnames which begin with TEXT.
- Initialize the hostname list the first time if neccessary.
- The array is malloc ()'ed, but not the individual strings. */
- static char **
- hostnames_matching (text)
- char *text;
- {
- register int i, len = strlen (text);
- register int begin, end;
- int last_search = -1;
- char **result = (char **)NULL;
-
- if (!hostname_list_initialized)
- {
- initialize_hostname_list ();
-
- if (!hostname_list_initialized)
- return ((char **)NULL);
- }
-
- sort_hostname_list ();
-
- /* The list is sorted. Do a binary search on it for the first character
- in TEXT, and then grovel the names of interest. */
- begin = 0; end = hostname_list_length;
-
- /* Special case. If TEXT consists of nothing, then the whole list is
- what is desired. */
- if (!*text)
- {
- result = (char **)xmalloc ((1 + hostname_list_length) * sizeof (char *));
- for (i = 0; i < hostname_list_length; i++)
- result[i] = hostname_list[i];
- result[i] = (char *)NULL;
- return (result);
- }
-
- /* Scan until found, or failure. */
- while (end != begin)
- {
- int r = 0;
-
- i = ((end - begin) / 2) + begin;
- if (i == last_search)
- break;
-
- if (hostname_list[i] &&
- (r = strncmp (hostname_list[i], text, len)) == 0)
- {
- while (strncmp (hostname_list[i], text, len) == 0 && i) i--;
- if (strncmp (hostname_list[i], text, len) != 0) i++;
-
- begin = i;
- while (hostname_list[i] &&
- strncmp (hostname_list[i], text, len) == 0) i++;
- end = i;
-
- result = (char **)xmalloc ((1 + (end - begin)) * sizeof (char *));
- for (i = 0; i + begin < end; i++)
- result[i] = hostname_list[begin + i];
- result[i] = (char *)NULL;
- return (result);
- }
-
- last_search = i;
-
- if (r < 0)
- begin = i;
- else
- end = i;
- }
- return ((char **)NULL);
- }
-
- /* This is a K*rn shell style insert-last-arg function. The
- difference is that Bash puts stuff into the history file before
- expansion and file name generation, so we deal with exactly what the
- user typed. Those wanting the other behavior, at least for the last
- arg, can use `$_'. This also `knows' about how rl_yank_nth_arg treats
- `$'. */
- static void
- insert_last_arg (count, c)
- int count, c;
- {
- extern int rl_explicit_arg;
-
- if (rl_explicit_arg)
- rl_yank_nth_arg (count, c);
- else
- rl_yank_nth_arg ('$', c);
- }
-
- /* The equivalent of the K*rn shell C-o operate-and-get-next-history-line
- editing command. */
- static int saved_history_line_to_use = 0;
-
- static void
- set_saved_history ()
- {
- HIST_ENTRY *h;
-
- if (saved_history_line_to_use)
- {
- if (history_set_pos (saved_history_line_to_use))
- {
- h = current_history ();
- if (h)
- {
- rl_insert_text (h->line);
- /*
- * Get rid of any undo list created by the previous insert,
- * so the line won't totally be erased when the edits are
- * undone (they will be normally, because this is a history
- * line -- cf. readline.c: line 380 or so).
- */
- if (rl_undo_list)
- {
- free_undo_list ();
- rl_undo_list = (UNDO_LIST *)NULL;
- }
- }
- }
- }
- saved_history_line_to_use = 0;
- rl_startup_hook = (Function *)NULL;
- }
-
- static void
- operate_and_get_next (count, c)
- int count, c;
- {
- int where;
- extern int history_stifled, history_length, max_input_history;
-
- /* Accept the current line. */
- rl_newline ();
-
- /* Find the current line, and find the next line to use. */
- where = where_history ();
-
- if (history_stifled && (history_length >= max_input_history))
- saved_history_line_to_use = where;
- else
- saved_history_line_to_use = where + 1;
-
- rl_startup_hook = (Function *)set_saved_history;
- }
-
- /* **************************************************************** */
- /* */
- /* How To Do Shell Completion */
- /* */
- /* **************************************************************** */
-
- /* Do some completion on TEXT. The indices of TEXT in RL_LINE_BUFFER are
- at START and END. Return an array of matches, or NULL if none. */
- static char **
- attempt_shell_completion (text, start, end)
- char *text;
- int start, end;
- {
- int in_command_position = 0;
- char **matches = (char **)NULL;
- char *command_separator_chars = ";|&{(";
-
- /* Determine if this could be a command word. It is if it appears at
- the start of the line (ignoring preceding whitespace), or if it
- appears after a character that separates commands. It cannot be a
- command word if we aren't at the top-level prompt. */
- {
- register int ti = start - 1;
-
- while ((ti > -1) && (whitespace (rl_line_buffer[ti])))
- ti--;
-
- if (ti < 0)
- {
- extern char *current_prompt_string, *ps1_prompt;
-
- /* Only do command completion at the start of a line when we
- are not prompting at top level. */
- if (current_prompt_string == ps1_prompt)
- in_command_position++;
- }
- else
- {
- if (member (rl_line_buffer[ti], command_separator_chars))
- in_command_position++;
- }
- }
-
- /* Variable name? */
- if (*text == '$')
- matches = completion_matches (text, variable_completion_function);
-
- /* If the word starts in `~', and there is no slash in the word, then
- try completing this word as a username. */
- if (!matches && *text == '~' && !index (text, '/'))
- matches = completion_matches (text, username_completion_function);
-
- /* Another one. Why not? If the word starts in '@', then look through
- the world of known hostnames for completion first. */
- if (!matches && *text == '@')
- matches = completion_matches (text, hostname_completion_function);
-
- /* And last, (but not least) if this word is in a command position, then
- complete over possible command names, including aliases, functions,
- and command names. */
- if (!matches && in_command_position && *text != '/')
- matches = completion_matches (text, command_word_completion_function);
-
- return (matches);
- }
-
- /* This is the function to call when the word to complete is at the start
- of a line. It grovels $PATH, looking for commands that match. It also
- scans aliases, function names, and the shell_builtin table. */
- static char *
- command_word_completion_function (hint_text, state)
- char *hint_text;
- int state;
- {
- extern SHELL_VAR **all_visible_functions ();
- char *extract_colon_unit ();
- static char *hint = (char *)NULL;
- static char *path = (char *)NULL;
- static char *val = (char *)NULL;
- static char *filename_hint = (char *)NULL;
- static int path_index, hint_len, istate;
- static int mapping_over, local_index;
- static SHELL_VAR **varlist = (SHELL_VAR **)NULL;
-
- /* We have to map over the possibilities for command words. If we have
- no state, then make one just for that purpose. */
-
- if (!state)
- {
- if (hint)
- free (hint);
-
- path = get_string_value ("PATH");
- path_index = 0;
-
- hint = savestring (hint_text);
- hint_len = strlen (hint);
-
- mapping_over = 0;
- val = (char *)NULL;
-
- /* Initialize the variables for each type of command word. */
- local_index = 0;
-
- if (varlist)
- free (varlist);
-
- varlist = all_visible_functions ();
- }
-
- /* mapping_over says what we are currently hacking. Note that every case
- in this list must fall through when there are no more possibilities. */
-
- switch (mapping_over)
- {
- case 0: /* Aliases come first. */
- #if defined (ALIAS)
- while (aliases && aliases[local_index])
- {
- register char *alias;
-
- alias = aliases[local_index++]->name;
-
- if (strncmp (alias, hint, hint_len) == 0)
- return (savestring (alias));
- }
- #endif /* ALIAS */
- local_index = 0;
- mapping_over++;
-
- case 1: /* Then shell reserved words. */
- {
- extern STRING_INT_ALIST word_token_alist[];
-
- while (word_token_alist[local_index].word)
- {
- register char *reserved_word;
-
- reserved_word = word_token_alist[local_index++].word;
-
- if (strncmp (reserved_word, hint, hint_len) == 0)
- return (savestring (reserved_word));
- }
- local_index = 0;
- mapping_over++;
- }
-
- case 2: /* Then function names. */
- while (varlist && varlist[local_index])
- {
- register char *varname;
-
- varname = varlist[local_index++]->name;
-
- if (strncmp (varname, hint, hint_len) == 0)
- return (savestring (varname));
- }
- local_index = 0;
- mapping_over++;
-
- case 3: /* Then shell builtins. */
- for (; local_index < num_shell_builtins; local_index++)
- {
- if (!shell_builtins[local_index].function ||
- !shell_builtins[local_index].enabled)
- continue;
-
- if (strncmp
- (shell_builtins[local_index].name, hint, hint_len) == 0)
- {
- int i = local_index++;
-
- return (savestring (shell_builtins[i].name));
- }
- }
- local_index = 0;
- mapping_over++;
- }
-
- /* Repeatedly call filename_completion_function while we have
- members of PATH left. Question: should we stat each file?
- Answer: we call executable_file () on each file. */
- outer:
-
- istate = (val != (char *)NULL);
-
- if (!istate)
- {
- char *current_path;
-
- /* Get the next directory from the path. If there is none, then we
- are all done. */
- if (!path ||
- !path[path_index] ||
- !(current_path = extract_colon_unit (path, &path_index)))
- return ((char *)NULL);
-
- if (!*current_path)
- {
- free (current_path);
- current_path = savestring (".");
- }
-
- if (*current_path == '~')
- {
- extern char *tilde_expand ();
- char *t;
-
- t = tilde_expand (current_path);
- free (current_path);
- current_path = t;
- }
-
- if (filename_hint)
- free (filename_hint);
-
- filename_hint =
- (char *)xmalloc (2 + strlen (current_path)
- + strlen (hint));
- sprintf (filename_hint, "%s/%s", current_path, hint);
-
- free (current_path);
- }
-
- inner:
- val = filename_completion_function (filename_hint, istate);
- istate = 1;
-
- if (!val)
- {
- goto outer;
- }
- else
- {
- char *rindex (), *temp = rindex (val, '/');
- temp++;
- if ((strncmp (hint, temp, hint_len) == 0) && executable_file (val))
- {
- temp = (savestring (temp));
- free (val);
- val = ""; /* So it won't be NULL. */
- return (temp);
- }
- else
- {
- free (val);
- goto inner;
- }
- }
- }
-
- /* Okay, now we write the entry_function for variable completion. */
- static char *
- variable_completion_function (text, state)
- int state;
- char *text;
- {
- register SHELL_VAR *var = (SHELL_VAR *)NULL;
- static SHELL_VAR **varlist = (SHELL_VAR **)NULL;
- static int varlist_index;
- static char *varname = (char *)NULL;
- static int namelen;
- static int first_char, first_char_loc;
-
- extern SHELL_VAR **all_visible_variables ();
-
- if (!state)
- {
- if (varname)
- free (varname);
-
- first_char_loc = 0;
- first_char = text[0];
-
- if (first_char == '$')
- first_char_loc++;
-
- varname = savestring (&text[first_char_loc]);
-
- namelen = strlen (varname);
- if (varlist)
- free (varlist);
- varlist = all_visible_variables ();
- varlist_index = 0;
- }
-
- while (varlist && varlist[varlist_index])
- {
- var = varlist[varlist_index];
-
- /* Compare. You can't do better than Zayre. No text is also
- a match. */
- if (!*varname || (strncmp (varname, var->name, namelen) == 0))
- break;
- varlist_index++;
- }
-
- if (!varlist || !varlist[varlist_index])
- {
- return ((char *)NULL);
- }
- else
- {
- char *value = (char *)xmalloc (2 + strlen (var->name));
-
- if (first_char_loc)
- *value = first_char;
-
- strcpy (&value[first_char_loc], var->name);
-
- varlist_index++;
- return (value);
- }
- }
-
- /* How about a completion function for hostnames? */
- static char *
- hostname_completion_function (text, state)
- int state;
- char *text;
- {
- static char **list = (char **)NULL;
- static int list_index = 0;
- static int first_char, first_char_loc;
-
- /* If we don't have any state, make some. */
- if (!state)
- {
- if (list)
- free (list);
-
- list = (char **)NULL;
-
- first_char_loc = 0;
- first_char = *text;
-
- if (first_char == '@')
- first_char_loc++;
-
- list = hostnames_matching (&text[first_char_loc]);
- list_index = 0;
- }
-
- if (list && list[list_index])
- {
- char *t = (char *)xmalloc (2 + strlen (list[list_index]));
-
- *t = first_char;
- strcpy (t + first_char_loc, list[list_index]);
- list_index++;
- return (t);
- }
- else
- return ((char *)NULL);
- }
-
- /* History and alias expand the line. But maybe do more? This
- is a test to see what users like. Do expand_string on the string. */
- static void
- shell_expand_line (ignore)
- int ignore;
- {
- char *pre_process_line (), *new_line;
-
- new_line = pre_process_line (rl_line_buffer, 0, 0);
-
- #if defined (ALIAS)
- if (new_line)
- {
- char *alias_expand (), *alias_line;
-
- alias_line = alias_expand (new_line);
- free (new_line);
- new_line = alias_line;
- }
- #endif /* ALIAS */
-
- if (new_line)
- {
- int old_point = rl_point;
- int at_end = rl_point == rl_end;
-
- /* If the line was history and alias expanded, then make that
- be one thing to undo. */
-
- if (strcmp (new_line, rl_line_buffer) != 0)
- {
- rl_point = rl_end;
-
- rl_add_undo (UNDO_BEGIN, 0, 0, 0);
- rl_kill_text (0, rl_point);
- rl_point = rl_end = 0;
- rl_insert_text (new_line);
- rl_add_undo (UNDO_END, 0, 0, 0);
- }
-
- free (new_line);
-
- /* If there is variable expansion to perform, do that as a separate
- operation to be undone. */
- {
- extern char *string_list ();
- extern WORD_LIST *expand_string ();
- WORD_LIST *expanded_string;
- char *string_list ();
-
- expanded_string = expand_string (rl_line_buffer, 0);
- if (!expanded_string)
- new_line = savestring ("");
- else
- {
- new_line = string_list (expanded_string);
- dispose_words (expanded_string);
- }
-
- if (strcmp (new_line, rl_line_buffer) != 0)
- {
- rl_add_undo (UNDO_BEGIN, 0, 0 ,0);
- rl_kill_text (0, rl_end);
- rl_point = rl_end = 0;
- rl_insert_text (new_line);
- rl_add_undo (UNDO_END, 0, 0, 0);
- }
-
- free (new_line);
-
- /* Place rl_point where we think it should go. */
- if (at_end)
- rl_point = rl_end;
- else if (old_point < rl_end)
- {
- rl_point = old_point;
- if (!whitespace (rl_line_buffer[rl_point]))
- rl_forward_word (1);
- }
- }
- }
- else
- {
- /* There was an error in expansion. Let the preprocessor print
- the error here. Note that we know that pre_process_line ()
- will return NULL, since it just did. */
- fprintf (rl_outstream, "\n\r");
- pre_process_line (rl_line_buffer, 1, 0);
- rl_forced_update_display ();
- }
- }
-
- /* Filename completion ignore. Emulates the "fignore" facility of
- tcsh. If FIGNORE is set, then don't match files with the
- given suffixes. If only one of the possibilities has an acceptable
- suffix, delete the others, else just return and let the completer
- signal an error. It is called by the completer when real
- completions are done on filenames by the completer's internal
- function, not for completion lists (M-?) and not on "other"
- completion types, such as hostnames or commands.
-
- It is passed a NULL-terminated array of (char *)'s that must be
- free()'d if they are deleted. The first element (names[0]) is the
- least-common-denominator string of the matching patterns (i.e.
- u<TAB> produces names[0] = "und", names[1] = "under.c", names[2] =
- "undun.c", name[3] = NULL). */
-
- struct ign {
- char *val;
- int len;
- };
-
- static struct ign *ignores; /* Store the ignore strings here */
- static int num_ignores; /* How many are there? */
- static char *last_fignore; /* Last value of fignore - cached for speed */
- extern char *extract_colon_unit (), *get_string_value ();
-
- static void
- setup_ignore_patterns ()
- {
- int numitems, maxitems, ptr;
- char *colon_bit;
- struct ign *p;
-
- char *this_fignore = get_string_value ("FIGNORE");
-
- /* If nothing has changed then just exit now. */
- if (this_fignore &&
- last_fignore &&
- strcmp (this_fignore, last_fignore) == 0 ||
- (!this_fignore && !last_fignore))
- {
- return;
- }
-
- /* Oops. FIGNORE has changed. Re-parse it. */
- num_ignores = 0;
-
- if (ignores)
- {
- for (p = ignores; p->val; p++) free(p->val);
- free (ignores);
- ignores = (struct ign*)NULL;
- }
-
- if (last_fignore)
- free (last_fignore);
-
- last_fignore = savestring (this_fignore);
-
- if (!this_fignore || !*this_fignore)
- return;
-
- numitems = maxitems = ptr = 0;
-
- while (colon_bit = extract_colon_unit (this_fignore, &ptr))
- {
- if (numitems + 1 > maxitems)
- ignores = (struct ign *)
- xrealloc (ignores, (maxitems += 10) * sizeof (struct ign));
-
- ignores[numitems].val = colon_bit;
- ignores[numitems].len = strlen (colon_bit);
- numitems++;
- }
- ignores[numitems].val = NULL;
- num_ignores = numitems;
- }
-
- static int
- name_is_acceptable (name)
- char *name;
- {
- struct ign *p;
- int nlen = strlen (name);
-
- for (p = ignores; p->val; p++)
- {
- if (nlen > p->len && p->len > 0 &&
- strcmp (p->val, &name[nlen - p->len]) == 0)
- return (0);
- }
-
- return (1);
- }
-
- static void
- filename_completion_ignore (names)
- char **names;
- {
- char **p;
- int idx;
-
- setup_ignore_patterns ();
-
- if (!num_ignores)
- return;
-
- for (p = names + 1, idx = -1; *p; p++)
- {
- if (name_is_acceptable (*p))
- {
- if (idx == -1) /* First match found. */
- idx = p - names;
- else
- return; /* Too many matches. */
- }
- }
-
- /* If none are acceptable then let the completer handle it. */
- if (idx == -1)
- return;
-
- /* Delete all non-matching elements. */
- free (names[0]);
- for (p = names + 1; *p; p++)
- {
- if (idx == (p - names))
- names[0] = *p;
- else
- free (*p);
-
- *p = NULL;
- }
- }
-
- /* Handle symbolic link references while hacking completion. */
- static void
- bash_symbolic_link_hook (dirname)
- char **dirname;
- {
- extern int follow_symbolic_links;
- char *make_absolute (), *temp_dirname;
-
- if (follow_symbolic_links && (strcmp (*dirname, ".") != 0))
- {
- temp_dirname = make_absolute (*dirname, get_working_directory (""));
-
- if (temp_dirname)
- {
- free (*dirname);
- *dirname = temp_dirname;
- }
- }
- }
-
- #if defined (DYNAMIC_HISTORY_COMPLETION)
- static char **history_completion_array = (char **)NULL;
- static int harry_size = 0;
- static int harry_len = 0;
-
- static void
- build_history_completion_array ()
- {
- register int i;
-
- /* First, clear out the current dynamic history completion list. */
- if (harry_size)
- {
- for (i = 0; history_completion_array[i]; i++)
- free (history_completion_array[i]);
-
- free (history_completion_array);
-
- history_completion_array = (char **)NULL;
- harry_size = 0;
- harry_len = 0;
- }
-
- /* Next, grovel each line of history, making each shell-sized token
- a separate entry in the history_completion_array. */
- {
- HIST_ENTRY **hlist;
-
- hlist = history_list ();
-
- if (hlist)
- {
- extern int qsort_string_compare ();
- register int j;
-
- for (i = 0; hlist[i]; i++)
- {
- extern char **history_tokenize ();
- char **tokens;
-
- /* Separate each token, and place into an array. */
- tokens = history_tokenize (hlist[i]->line);
-
- for (j = 0; tokens && tokens[j]; j++)
- {
- if (harry_len + 2 > harry_size)
- history_completion_array = (char **) xrealloc
- (history_completion_array,
- (harry_size += 10) * sizeof (char *));
-
- history_completion_array[harry_len++] = tokens[j];
- history_completion_array[harry_len] = (char *)NULL;
- }
- free (tokens);
- }
-
- /* Sort the complete list of tokens. */
- qsort (history_completion_array, harry_len, sizeof (char *),
- qsort_string_compare);
-
- /* Instead of removing the duplicate entries here, we let the
- code in the completer handle it. */
- }
- }
- }
-
- static char *
- history_completion_generator (hint_text, state)
- char *hint_text;
- int state;
- {
- static int local_index = 0;
- static char *text = (char *)NULL;
- static int len = 0;
-
- /* If this is the first call to the generator, then initialize the
- list of strings to complete over. */
- if (!state)
- {
- local_index = 0;
- build_history_completion_array ();
- text = hint_text;
- len = strlen (text);
- }
-
- while (history_completion_array && history_completion_array[local_index])
- {
- if (strncmp (text, history_completion_array[local_index++], len) == 0)
- return (savestring (history_completion_array[local_index - 1]));
- }
- return ((char *)NULL);
- }
-
- static void
- dynamic_complete_history (count, key)
- int count, key;
- {
- extern Function *rl_last_func;
- Function *orig_func;
- Function *orig_attempt_func;
-
- orig_func = rl_completion_entry_function;
- orig_attempt_func = rl_attempted_completion_function;
- rl_completion_entry_function = (Function *)history_completion_generator;
- rl_attempted_completion_function = (Function *)0x0;
-
- if (rl_last_func == (Function *)dynamic_complete_history)
- rl_complete_internal ('?');
- else
- rl_complete_internal (TAB);
-
- rl_completion_entry_function = orig_func;
- rl_attempted_completion_function = orig_attempt_func;
- }
-
- #endif /* DYNAMIC_HISTORY_COMPLETION */
-
- #if defined (SPECIFIC_COMPLETION_FUNCTIONS)
- static void
- bash_complete_username (ignore, ignore2)
- int ignore, ignore2;
- {
- bash_complete_username_internal (TAB);
- }
-
- static void
- bash_possible_username_completions (ignore, ignore2)
- int ignore, ignore2;
- {
- bash_complete_username_internal ('?');
- }
-
- static void
- bash_complete_username_internal (what_to_do)
- int what_to_do;
- {
- bash_specific_completion
- (what_to_do, (Function *)username_completion_function);
- }
-
- static void
- bash_complete_filename (ignore, ignore2)
- int ignore, ignore2;
- {
- bash_complete_filename_internal (TAB);
- }
-
- static void
- bash_possible_filename_completions (ignore, ignore2)
- int ignore, ignore2;
- {
- bash_complete_filename_internal ('?');
- }
-
- static void
- bash_complete_filename_internal (what_to_do)
- int what_to_do;
- {
- Function *orig_func;
- Function *orig_attempt_func;
- char *orig_rl_completer_word_break_characters;
- extern char *rl_completer_word_break_characters;
-
- orig_func = rl_completion_entry_function;
- orig_attempt_func = rl_attempted_completion_function;
- orig_rl_completer_word_break_characters = rl_completer_word_break_characters;
- rl_completion_entry_function = (Function *)filename_completion_function;
- rl_attempted_completion_function = (Function *)0x0;
- rl_completer_word_break_characters = " \t\n\"\'";
-
- rl_complete_internal (what_to_do);
-
- rl_completion_entry_function = orig_func;
- rl_attempted_completion_function = orig_attempt_func;
- rl_completer_word_break_characters = orig_rl_completer_word_break_characters;
- }
-
- static void
- bash_complete_hostname (ignore, ignore2)
- int ignore, ignore2;
- {
- bash_complete_hostname_internal (TAB);
- }
-
- static void
- bash_possible_hostname_completions (ignore, ignore2)
- int ignore, ignore2;
- {
- bash_complete_hostname_internal ('?');
- }
-
- static void
- bash_complete_variable (ignore, ignore2)
- int ignore, ignore2;
- {
- bash_complete_variable_internal (TAB);
- }
-
- static void
- bash_possible_variable_completions (ignore, ignore2)
- int ignore, ignore2;
- {
- bash_complete_variable_internal ('?');
- }
-
-
- static void
- bash_complete_command (ignore, ignore2)
- int ignore, ignore2;
- {
- bash_complete_command_internal (TAB);
- }
-
- static void
- bash_possible_command_completions (ignore, ignore2)
- int ignore, ignore2;
- {
- bash_complete_command_internal ('?');
- }
-
- static void
- bash_complete_hostname_internal (what_to_do)
- int what_to_do;
- {
- bash_specific_completion
- (what_to_do, (Function *)hostname_completion_function);
- }
-
- static void
- bash_complete_variable_internal (what_to_do)
- int what_to_do;
- {
- bash_specific_completion
- (what_to_do, (Function *)variable_completion_function);
- }
-
- static void
- bash_complete_command_internal (what_to_do)
- int what_to_do;
- {
- bash_specific_completion
- (what_to_do, (Function *)command_word_completion_function);
- }
-
- static void
- bash_specific_completion (what_to_do, generator)
- int what_to_do;
- Function *generator;
- {
- Function *orig_func;
- Function *orig_attempt_func;
-
- orig_func = rl_completion_entry_function;
- orig_attempt_func = rl_attempted_completion_function;
- rl_completion_entry_function = generator;
- rl_attempted_completion_function = (Function *)0x0;
-
- rl_complete_internal (what_to_do);
-
- rl_completion_entry_function = orig_func;
- rl_attempted_completion_function = orig_attempt_func;
- }
-
- #endif /* SPECIFIC_COMPLETION_FUNCTIONS */
-
-